package com.time.admin.domain.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.time.admin.common.entity.PageResult;
import com.time.admin.common.enums.IsDeletedFlagEnum;
import com.time.admin.domain.constants.ExceptionCode;
import com.time.admin.domain.converter.PostBOConverter;
import com.time.admin.domain.entity.PostBO;
import com.time.admin.domain.exception.InsertDataException;
import com.time.admin.domain.exception.NotFoundDomainException;
import com.time.admin.domain.redis.RedisUtil;
import com.time.admin.domain.service.AdminPostDomainService;
import com.time.admin.domain.utils.MarkdownStatisticsUtil;
import com.time.admin.infra.basic.entity.*;
import com.time.admin.infra.basic.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 文章管理领域服务层接口实现类
 *
 * @author: HuangQi
 * @date: Created in 9:56 2025/11/4
 * @description: 文章管理领域服务层接口实现
 */
@Slf4j
@Service("adminPostDomainService")
public class AdminPostDomainServiceImpl implements AdminPostDomainService {

    @Autowired
    private PostService postService;

    @Autowired
    private TagService tagService;

    @Autowired
    private PostTagService postTagService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private PostCategoryService postCategoryService;

    @Autowired
    private PostDetailService postDetailService;

    @Autowired
    private PostMetricService postMetricService;

    @Autowired
    private RedisUtil redisUtil;

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final long POST_CACHE_TIMEOUT_MINUTES = 10L;
    private static final long POST_CACHE_JITTER_MINUTES = 5L;
    private static final TimeUnit POST_CACHE_TIMEUNIT = TimeUnit.MINUTES;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(PostBO postBO) {
        if (log.isInfoEnabled()) {
            log.info("AdminPostDomainServiceImpl.add.bo:{}", postBO);
        }

        // 处理文章标签和分类信息
        process_post_tag_and_categroy(postBO, false);

        // 构建文章详情，插入文章详情表
        PostDetail postDetail = new PostDetail();
        postDetail.setPostId(postBO.getPostId());
        postDetail.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        if (postDetailService.insert(postDetail) <= 0) {
            throw new InsertDataException(ExceptionCode.POST_DETAIL_INSERT_ERROR, "文章详情插入失败");
        }

        // 构建文章交互数据统计信息，插入文章交互信息统计表
        PostMetric postMetric = new PostMetric();
        postMetric.setPostId(postBO.getPostId());
        postMetric.setWordCount(MarkdownStatisticsUtil.countArticleLength(postBO.getContent()));
        postMetric.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        if (postMetricService.insert(postMetric) <= 0) {
            throw new InsertDataException(ExceptionCode.POST_METRIC_INSERT_ERROR, "文章交互数据统计信息插入失败");
        }

        // 调用 service，插入文章表
        Post post = PostBOConverter.INSTANCE.convertBOToEntity(postBO);
        post.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        int count = postService.insert(post);

        return count > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(PostBO postBO) {
        if (log.isInfoEnabled()) {
            log.info("AdminPostDomainServiceImpl.update.bo:{}", postBO);
        }

        // 处理文章标签和分类信息
        process_post_tag_and_categroy(postBO, true);

        // 参数转换，调用 service
        Post post = PostBOConverter.INSTANCE.convertBOToEntity(postBO);
        int count = postService.update(post);

        // 返回结果
        return count > 0;
    }

    /**
     * 处理文章标签和分类信息
     *
     * @param postBO          文章信息
     * @param need_delete_old 是否需要删除旧的关联关系
     */
    private void process_post_tag_and_categroy(PostBO postBO, boolean need_delete_old) {
        // 根据分类名称获取分类ID
        String categoryName = postBO.getCategoryName();
        Category category = categoryService.queryByCategoryName(categoryName);
        if (Objects.isNull(category) && !need_delete_old) {
            throw new NotFoundDomainException(ExceptionCode.CATEGORY_NOT_EXISTS, "文章分类不存在");
        }

        // 根据标签名称列表获取标签ID列表
        List<String> tagNameList = postBO.getTagNameList();
        List<Tag> tagList = tagNameList.stream()
                .map(tagName -> tagService.queryByTagName(tagName))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(tagList) && !need_delete_old) {
            throw new NotFoundDomainException(ExceptionCode.TAG_NOT_EXISTS, "文章标签不存在");
        }

        // 删除旧的文章分类关联关系
        if (need_delete_old) {
            postCategoryService.deleteByPostId(postBO.getPostId());
        }

        // 构建文章-分类关系，插入文章-分类关系表
        PostCategory postCategory = new PostCategory();
        postCategory.setPostId(postBO.getPostId());
        postCategory.setCategoryId(category.getCategoryId());
        postCategory.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        if (postCategoryService.insert(postCategory) <= 0) {
            throw new InsertDataException(ExceptionCode.POST_CATEGORY_INSERT_ERROR, "文章分类关联关系插入失败");
        }

        // 删除旧的文章标签关联关系
        if (need_delete_old) {
            postTagService.deleteByPostId(postBO.getPostId());
        }

        // 构建文章-标签关系，插入文章-标签关系表
        List<PostTag> postTagList = new LinkedList<>();
        for (Tag tag : tagList) {
            PostTag postTag = new PostTag();
            postTag.setPostId(postBO.getPostId());
            postTag.setTagId(tag.getTagId());
            postTag.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
            postTagList.add(postTag);
        }
        if (postTagService.batchInsert(postTagList) != postTagList.size()) {
            throw new InsertDataException(ExceptionCode.POST_TAG_INSERT_ERROR, "文章标签关联关系插入失败");
        }
    }

    @Override
    public Boolean delete(Long id) {
        if (log.isInfoEnabled()) {
            log.info("AdminPostDomainServiceImpl.delete.id:{}", id);
        }
        // 删除文章分类关联信息
        if (postCategoryService.deleteByPostId(id) <= 0) {
            throw new InsertDataException(ExceptionCode.POST_CATEGORY_DELETE_ERROR, "文章分类关联关系删除失败");
        }

        // 删除文章标签关联信息
        if (postTagService.deleteByPostId(id) <= 0) {
            throw new InsertDataException(ExceptionCode.POST_TAG_DELETE_ERROR, "文章标签关联关系删除失败");
        }

        // 删除文章详情信息
        if (postDetailService.deleteByPostId(id) <= 0) {
            throw new InsertDataException(ExceptionCode.POST_DETAIL_DELETE_ERROR, "文章详情删除失败");
        }

        // 删除文章交互数据统计信息
        if (postMetricService.deleteByPostId(id) <= 0) {
            throw new InsertDataException(ExceptionCode.POST_METRIC_DELETE_ERROR, "文章交互数据统计信息删除失败");
        }

        // 删除文章信息
        int count = postService.deleteById(id);

        // 返回结果
        return count > 0;
    }

    @Override
    public PageResult<PostBO> queryAllPost(PostBO postBO) {
        if (log.isInfoEnabled()) {
            log.info("AdminPostDomainServiceImpl.queryAllPost.bo:{}", postBO);
        }

        String cacheKey = redisUtil.buildKey("admin", "post", "queryAll", serializeKey(postBO));
        PageResult<PostBO> cacheResult = getCache(cacheKey, new TypeReference<PageResult<PostBO>>() {});
        if (cacheResult != null) {
            return cacheResult;
        }

        // 参数转换
        Post post = PostBOConverter.INSTANCE.convertBOToEntity(postBO);
        post.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        // 构建分页结果
        PageResult<PostBO> pageResult = new PageResult<>();
        pageResult.setPageNo(postBO.getPageNo());
        pageResult.setPageSize(postBO.getPageSize());

        // 获取总记录数和分页的文章信息
        int total = 0;
        List<Post> postList = new LinkedList<>();

        if (Objects.isNull(postBO.getCategoryName())) {
            // 获取总记录数
            total = postService.countByCondition(post);
            // 获取分页的文章信息（使用PageResult自动计算的start）
            pageResult.setTotal(total);
            int start = (pageResult.getStart() - 1); // PageResult的start是从1开始的，需要转换为从0开始
            postList = postService.pageQueryByCondition(post, start, pageResult.getPageSize());
        } else {
            // 根据分类名称，查询分类信息
            Category category = categoryService.queryByCategoryName(postBO.getCategoryName());
            if (Objects.isNull(category)) {
                // 分类不存在，返回空结果
                pageResult.setTotal(0);
                pageResult.setResult(Collections.emptyList());
                setCache(cacheKey, pageResult);
                return pageResult;
            }

            total = postService.countByCategoryId(post, category.getCategoryId());
            // 获取分页的文章信息（使用PageResult自动计算的start）
            pageResult.setTotal(total);
            int start = (pageResult.getStart() - 1); // PageResult的start是从1开始的，需要转换为从0开始
            postList = postService.pageQueryByCategoryId(post, category.getCategoryId(), start, pageResult.getPageSize());
        }

        // 如果查询结果为空，直接返回
        if (CollectionUtils.isEmpty(postList)) {
            pageResult.setResult(Collections.emptyList());
            setCache(cacheKey, pageResult);
            return pageResult;
        }

        // 移除文章信息的正文信息（避免返回的结果太大了）
        postList.forEach(item -> {
            item.setContent("");
        });

        // 将查询的信息组装到 postBO 中
        List<PostBO> postBOList = PostBOConverter.INSTANCE.convertEntityListToBO(postList);

        // 收集所有文章ID，用于批量查询优化
        List<Long> postIdList = postBOList.stream()
                .map(PostBO::getPostId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 批量查询文章分类关联信息
        Map<Long, PostCategory> postCategoryMap = new HashMap<>();
        Map<Long, Category> categoryMap = new HashMap<>();
        for (Long postId : postIdList) {
            PostCategory postCategory = postCategoryService.queryByPostId(postId);
            if (Objects.nonNull(postCategory)) {
                postCategoryMap.put(postId, postCategory);
                // 批量查询分类信息（避免重复查询相同分类）
                if (!categoryMap.containsKey(postCategory.getCategoryId())) {
                    Category category = categoryService.queryById(postCategory.getCategoryId());
                    if (Objects.nonNull(category)) {
                        categoryMap.put(postCategory.getCategoryId(), category);
                    }
                }
            }
        }

        // 批量查询文章标签关联信息
        Map<Long, List<PostTag>> postTagMap = new HashMap<>();
        Set<Long> tagIdSet = new HashSet<>();
        for (Long postId : postIdList) {
            List<PostTag> postTagList = postTagService.queryByPostId(postId);
            if (CollectionUtils.isNotEmpty(postTagList)) {
                postTagMap.put(postId, postTagList);
                // 收集所有标签ID
                postTagList.forEach(pt -> tagIdSet.add(pt.getTagId()));
            }
        }

        // 批量查询标签信息
        Map<Long, Tag> tagMap = new HashMap<>();
        for (Long tagId : tagIdSet) {
            Tag tag = tagService.queryById(tagId);
            if (Objects.nonNull(tag)) {
                tagMap.put(tagId, tag);
            }
        }

        // 批量查询文章详情信息
        Map<Long, PostDetail> postDetailMap = new HashMap<>();
        for (Long postId : postIdList) {
            PostDetail postDetail = postDetailService.queryByPostId(postId);
            if (Objects.nonNull(postDetail)) {
                postDetailMap.put(postId, postDetail);
            }
        }

        // 组装数据到 postBO 中
        postBOList.forEach(pb -> {
            Long postId = pb.getPostId();

            // 设置分类信息
            PostCategory postCategory = postCategoryMap.get(postId);
            if (Objects.nonNull(postCategory)) {
                Category category = categoryMap.get(postCategory.getCategoryId());
                if (Objects.nonNull(category)) {
                    pb.setCategoryName(category.getCategoryName());
                } else {
                    pb.setCategoryName("");
                }
            } else {
                pb.setCategoryName("");
            }

            // 设置标签信息
            List<PostTag> postTagList = postTagMap.get(postId);
            if (CollectionUtils.isNotEmpty(postTagList)) {
                List<String> tagNameList = postTagList.stream()
                        .map(PostTag::getTagId)
                        .map(tagMap::get)
                        .filter(Objects::nonNull)
                        .map(Tag::getTagName)
                        .collect(Collectors.toList());
                pb.setTagNameList(tagNameList);
            } else {
                pb.setTagNameList(Collections.emptyList());
            }

            // 设置文章详情信息（浏览量）
            PostDetail postDetail = postDetailMap.get(postId);
            if (Objects.nonNull(postDetail)) {
                pb.setViewCount(postDetail.getViews());
            } else {
                pb.setViewCount(0L);
            }
        });

        // 设置分页结果
        pageResult.setResult(postBOList);

        setCache(cacheKey, pageResult);
        // 返回结果
        return pageResult;
    }

    @Override
    public PostBO queryPost(Long id) {
        if (log.isInfoEnabled()) {
            log.info("AdminPostDomainServiceImpl.queryPost.id:{}", id);
        }

        String cacheKey = redisUtil.buildKey("admin", "post", "detail", String.valueOf(id));
        PostBO cacheResult = getCache(cacheKey, new TypeReference<PostBO>() {});
        if (cacheResult != null) {
            return cacheResult;
        }

        // 获取文章信息
        Post post = postService.queryById(id);
        if (Objects.isNull(post)) {
            throw new NotFoundDomainException(ExceptionCode.POST_NOT_EXISTS, "该文章不存在");
        }

        // 获取分类和标签信息
        String categoryName = "";
        PostCategory postCategory = postCategoryService.queryByPostId(id);
        if (!Objects.isNull(postCategory)) {
            Category category = categoryService.queryById(postCategory.getCategoryId());
            categoryName = category.getCategoryName();
        }

        List<Tag> tagList = Optional.ofNullable(postTagService.queryByPostId(id))
                .orElseGet(Collections::emptyList)
                .stream()
                .map(pt -> tagService.queryById(pt.getTagId()))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        List<String> tagNameList = tagList.stream().map(Tag::getTagName).collect(Collectors.toList());

        // 组装 postBO 返回
        PostBO postBO = PostBOConverter.INSTANCE.convertEntityToBO(post);
        postBO.setCategoryName(categoryName);
        postBO.setTagNameList(tagNameList);

        setCache(cacheKey, postBO);
        return postBO;
    }

    /**
     * 从 Redis 缓存读取数据
     */
    private <T> T getCache(String key, TypeReference<T> typeReference) {
        try {
            String cacheValue = redisUtil.get(key);
            if (cacheValue == null) {
                return null;
            }
            return OBJECT_MAPPER.readValue(cacheValue,
                    OBJECT_MAPPER.getTypeFactory().constructType(typeReference.getType()));
        } catch (Exception e) {
            log.warn("AdminPostDomainServiceImpl.getCache.error:key={}, msg={}", key, e.getMessage());
            return null;
        }
    }

    /**
     * 写入 Redis 缓存，带随机过期时间，避免雪崩
     */
    private void setCache(String key, Object value) {
        try {
            long ttl = POST_CACHE_TIMEOUT_MINUTES +
                    ThreadLocalRandom.current().nextLong(POST_CACHE_JITTER_MINUTES + 1);
            redisUtil.set(key, OBJECT_MAPPER.writeValueAsString(value), ttl, POST_CACHE_TIMEUNIT);
        } catch (Exception e) {
            log.warn("AdminPostDomainServiceImpl.setCache.error:key={}, msg={}", key, e.getMessage());
        }
    }

    /**
     * 将查询条件序列化为缓存 key 片段
     */
    private String serializeKey(Object obj) {
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (Exception e) {
            log.warn("AdminPostDomainServiceImpl.serializeKey.error:{}", e.getMessage());
            return String.valueOf(obj);
        }
    }
}
